/*
 *  This file is part of LifeBudget - Copyright 2010 Clifton Eaton
 *  ------------------------------------------------------------------
 *  LifeBudget is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  LifeBudget is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with LifeBudget.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.lifebudget.app.server;

import java.util.ArrayList;
import java.util.List;

import org.lifebudget.app.client.mvc.model.Category;
import org.lifebudget.app.client.mvc.model.CategoryAmount;
import org.lifebudget.app.client.mvc.model.CategoryTotal;
import org.lifebudget.app.client.mvc.model.Transaction;
import org.lifebudget.app.client.mvc.model.TransactionData;
import org.lifebudget.app.client.mvc.model.Transaction.TransactionType;
import org.lifebudget.app.client.service.LoginService;
import org.lifebudget.app.client.service.TransactionService;
import org.lifebudget.app.persistence.dao.TransactionDAO;
import org.lifebudget.app.persistence.dao.TransactionItemDAO;
import org.lifebudget.app.persistence.repository.CategoryRepository;
import org.lifebudget.app.persistence.repository.PaycheckCalculatorRepository;
import org.lifebudget.app.persistence.repository.TransactionRepository;

import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

/**
 * The Class TransactionServiceImpl implements the {@link TransactionService} interface
 * and provides server-side functionality for GWT RPC.
 */
@RemoteServiceRelativePath("transaction")
public class TransactionServiceImpl extends RemoteServiceServlet implements TransactionService {
	
	/** The transaction repository. */
	TransactionRepository transactionRepo;
	
	/** The category repository. */
	private CategoryRepository categoryRepo;
	
	/** The paycheck repository. */
	private PaycheckCalculatorRepository paycheckRepo;

	/**
	 * Instantiates a new transaction service.
	 */
	public TransactionServiceImpl() {
		super();
		transactionRepo = new TransactionRepository();
		categoryRepo = new CategoryRepository();
		paycheckRepo = new PaycheckCalculatorRepository();
	}

	/* (non-Javadoc)
	 * @see org.lifebudget.app.client.service.TransactionService#getTransactionData()
	 */
	@Override
	public TransactionData getTransactionData() {
		TransactionData td = new TransactionData();
		td.setAllCategories(categoryRepo.getAllCategories(true));
		List<Transaction> transactions = transactionRepo.getAllTransactions();
		td.setTransactions(transactions);
		td.setAllPaycheckCalculators(paycheckRepo.getAllPaycheckCalculators());
		return td;
	}

	/* (non-Javadoc)
	 * @see org.lifebudget.app.client.service.TransactionService#saveTransactions(java.util.List)
	 */
	public List<Transaction> saveTransactions(List<Transaction> transactions) {

		if (transactions != null) {
			List<Transaction> updates = new ArrayList<Transaction>();

			// new transactions
			for (Transaction transaction : transactions) {
				// check for ids
				if (transaction.getId() == 0) {
					System.out.println("Adding Transaction " + transaction.getDescription());
					// persist them
					long newId = transactionRepo.addTransaction(transaction);
					transaction.setId(newId);
					List<TransactionItemDAO> tItems = createTransactionItemsFromTransaction(transaction);
					transactionRepo.saveTransactionItems(tItems);
				} else {
					System.out.println("Updating Transaction " + transaction.getDescription());
					updates.add(transaction);
				}

			}
			// updates to old ones
			for (Transaction transaction : updates) {
				// get old one, compare to see if associated element is
				// different if yes:
				TransactionDAO t = transactionRepo.getTransaction(transaction);
				boolean updateTransactionItems = true;
				if (t.getAmount() != transaction.getAmount()) {
					updateTransactionItems = true;
				} else if (t.getType().equals(TransactionType.Categorized.toString())) {
					if (t.getAmount() != transaction.getAmount()
							|| t.getCategoryId() != transaction.getCategory().getId()) {
						updateTransactionItems = true;
					}
				} else if (t.getType().equals(TransactionType.Paycheck.toString())) {
					if (t.getPaycheckId() != transaction.getPaycheckCalc().getId()) {
						updateTransactionItems = true;
					}
				}
				if (updateTransactionItems) {
					// remove all TransactionItems
					transactionRepo.removeAllTransactionItemsForTransaction(transaction.getId());

					/*
					 * if paycheck, create multiple new ones if category, create
					 * single one store new one
					 */

					List<TransactionItemDAO> tItems = createTransactionItemsFromTransaction(transaction);
					transactionRepo.saveTransactionItems(tItems);
				}
				transactionRepo.updateTransaction(transaction);

			}
		}

		return transactionRepo.getAllTransactions();
	}

	/* (non-Javadoc)
	 * @see org.lifebudget.app.client.service.TransactionService#removeTransaction(org.lifebudget.app.client.mvc.model.Transaction)
	 */
	public void removeTransaction(Transaction transaction) {
		System.out.println("In removeTransactions()");
		if (transaction.getId() != 0) {
			transactionRepo.deleteTransaction(transaction);
			transactionRepo.removeAllTransactionItemsForTransaction(transaction.getId());
		}
	}

	/**
	 * Creates the transaction items from transaction.
	 *
	 * @param t the t
	 * @return the list
	 */
	private List<TransactionItemDAO> createTransactionItemsFromTransaction(Transaction t) {
		List<TransactionItemDAO> transactionItems = new ArrayList<TransactionItemDAO>();
		if (t.getType() == TransactionType.Paycheck && t.getPaycheckCalc() != null) {
			createTransactionItemsFromPaycheckTransaction(t, transactionItems);

		} else if (t.getType() == TransactionType.Categorized && t.getCategory() != null) {
			// if category, then create single TransactionItem
			if (t.getCategory() != null) {
				TransactionItemDAO ti = new TransactionItemDAO();
				ti.setTransactionId(t.getId());
				ti.setAmount(t.getAmount());
				ti.setCategoryId(t.getCategory().getId());
				transactionItems.add(ti);
			}
		}
		return transactionItems;
	}

	/**
	 * Creates the transaction items from paycheck transaction.
	 *
	 * @param t the t
	 * @param transactionItems the transaction items
	 */
	private void createTransactionItemsFromPaycheckTransaction(Transaction t, List<TransactionItemDAO> transactionItems) {
		double tAmt = t.getAmount();
		List<CategoryAmount> categoryAmts = paycheckRepo.getAllRelatedCategoryAmounts(t.getPaycheckCalc().getId());
		for (CategoryAmount c : categoryAmts) {

			if (tAmt >= 0) {
				TransactionItemDAO ti = new TransactionItemDAO();
				if (tAmt >= c.getAmount()) {
					ti.setAmount(c.getAmount());
				} else if (tAmt > 0) {
					ti.setAmount(tAmt);
				}
				ti.setTransactionId(t.getId());
				ti.setCategoryId(c.getCategory().getId());
				transactionItems.add(ti);
				tAmt -= c.getAmount();
			}

		}
		if (tAmt > 0) {
			long defaultCategoryId = categoryRepo.getDefaultCategoryId();
			TransactionItemDAO ti = new TransactionItemDAO();
			ti.setTransactionId(t.getId());
			ti.setAmount(tAmt);
			ti.setCategoryId(defaultCategoryId);
			transactionItems.add(ti);
		}
	}

	/* (non-Javadoc)
	 * @see org.lifebudget.app.client.service.TransactionService#getCategoryTotals()
	 */
	@Override
	public List<CategoryTotal> getCategoryTotals() {
		List<CategoryTotal> totals = new ArrayList<CategoryTotal>();
		List<Category> categories = categoryRepo.getAllCategories(true);
		for (Category category : categories) {
			double amt = 0;
			List<TransactionItemDAO> tItems = transactionRepo.getallTransactionItemsForCategory(category.getId());
			for (TransactionItemDAO ti : tItems) {
				amt += ti.getAmount();
			}

			totals.add(new CategoryTotal(category, amt));
		}
		return totals;
	}

}
